﻿/*
 *  Author: Louis A. Leskoviansky
 *  Company: Artemis Solutions Group (http://www.artemis-solutions.com)
 */


using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Resources;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace AjaxControlFramework
{
    
	public abstract class CallbackStrategy : IAjaxControlStrategy
	{
		//------// Properties \\--------------------------------------------\\
		public virtual IAjaxControl Control { get; set; }
		public virtual CallbackSerializer Serializer { get; set; }
		public virtual ScriptStrategy Scripter { get; set; }


        private Page _parentPage = null;
        public virtual Page ParentPage
        {
            get
            {
                if (_parentPage == null)
                {
                    if (Control == null) { return null; }

                    if ((Control as Control).Page == null)
                    {
                        _parentPage = (HttpContext.Current.CurrentHandler as Page);
                    }
                    else
                    {
                        _parentPage = (Control as Control).Page;
                    }
                }

                return _parentPage;
            }
        }


		protected virtual Dictionary<string, object> CallbackResult { get; set; }
		public virtual EventHandler LoadHandler { get; set; }
        protected virtual bool ExceptionOccurred { get; set; }


        public virtual bool? AutoSaveControlState { get; set; }


        public delegate void InitRecursiveMethod(Control control, Control namingContainer);
        public delegate void LoadRecursiveMethod(Control control);
        public delegate void PreRenderRecursiveInternalMethod(Control control);
        public delegate void UnloadRecursiveMethod(Control control, bool dispose);


        private static Type TypeOfControl = typeof(System.Web.UI.Control);


        private static object InitRecursiveLock = new object();
        private static InitRecursiveMethod _initRecursive = null;
        public static InitRecursiveMethod InitRecursive
        {
            get
            {
                if (_initRecursive == null)
                {
                    lock (InitRecursiveLock)
                    {
                        if (_initRecursive == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "InitRecursive".
                            MethodInfo initRecursiveMethodInfo = TypeOfControl.GetMethod("InitRecursive", (BindingFlags.NonPublic | BindingFlags.Instance), null, new Type[] { TypeOfControl }, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("InitRecursive", typeof(void), new Type[] { TypeOfControl, TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldarg_1);
                            ilGenerator.Emit(OpCodes.Callvirt, initRecursiveMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _initRecursive = (InitRecursiveMethod)dynamicMethod.CreateDelegate(typeof(InitRecursiveMethod));
                        }
                    }
                }

                return _initRecursive;
            }
        }


        private static object LoadRecursiveLock = new object();
        private static LoadRecursiveMethod _loadRecursive = null;
        public static LoadRecursiveMethod LoadRecursive
        {
            get
            {
                if (_loadRecursive == null)
                {
                    lock (LoadRecursiveLock)
                    {
                        if (_loadRecursive == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "LoadRecursive".
                            MethodInfo loadRecursiveMethodInfo = TypeOfControl.GetMethod("LoadRecursive", (BindingFlags.NonPublic | BindingFlags.Instance), null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("LoadRecursive", typeof(void), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Callvirt, loadRecursiveMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _loadRecursive = (LoadRecursiveMethod)dynamicMethod.CreateDelegate(typeof(LoadRecursiveMethod));
                        }
                    }
                }

                return _loadRecursive;
            }
        }


        private static object PreRenderRecursiveInternalLock = new object();
        private static PreRenderRecursiveInternalMethod _preRenderRecursiveInternal = null;
        public static PreRenderRecursiveInternalMethod PreRenderRecursiveInternal
        {
            get
            {
                if (_preRenderRecursiveInternal == null)
                {
                    lock (PreRenderRecursiveInternalLock)
                    {
                        if (_preRenderRecursiveInternal == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "PreRenderRecursiveInternal".
                            MethodInfo preRenderRecursiveInternalMethodInfo = TypeOfControl.GetMethod("PreRenderRecursiveInternal", (BindingFlags.NonPublic | BindingFlags.Instance), null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("PreRenderRecursiveInternal", typeof(void), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Callvirt, preRenderRecursiveInternalMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _preRenderRecursiveInternal = (PreRenderRecursiveInternalMethod)dynamicMethod.CreateDelegate(typeof(PreRenderRecursiveInternalMethod));
                        }
                    }
                }

                return _preRenderRecursiveInternal;
            }
        }


        private static object UnloadRecursiveLock = new object();
        private static UnloadRecursiveMethod _unloadRecursive = null;
        public static UnloadRecursiveMethod UnloadRecursive
        {
            get
            {
                if (_unloadRecursive == null)
                {
                    lock (UnloadRecursiveLock)
                    {
                        if (_unloadRecursive == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "UnloadRecursive".
                            MethodInfo unloadRecursiveMethodInfo = TypeOfControl.GetMethod("UnloadRecursive", (BindingFlags.NonPublic | BindingFlags.Instance), null, new Type[] { typeof(bool) }, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("UnloadRecursive", typeof(void), new Type[] { TypeOfControl, typeof(bool) }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldarg_1);
                            ilGenerator.Emit(OpCodes.Callvirt, unloadRecursiveMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _unloadRecursive = (UnloadRecursiveMethod)dynamicMethod.CreateDelegate(typeof(UnloadRecursiveMethod));
                        }
                    }
                }

                return _unloadRecursive;
            }
        }


        public delegate EventHandlerList GetEventsProperty(Control control);


        private static object GetEventsLock = new object();
        private static GetEventsProperty _getEvents = null;
        public static GetEventsProperty GetEvents
        {
            get
            {
                if (_getEvents == null)
                {
                    lock (GetEventsLock)
                    {
                        if (_getEvents == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET protected property "Events".
                            MethodInfo getEventsMethodInfo = TypeOfControl.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);
                            DynamicMethod dynamicMethod = new DynamicMethod("GetEvents", typeof(EventHandlerList), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Call, getEventsMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _getEvents = (GetEventsProperty)dynamicMethod.CreateDelegate(typeof(GetEventsProperty));
                        }
                    }
                }

                return _getEvents;
            }
        }


        public delegate void SetParentField(Control control, Control parent);


        private static object SetParentLock = new object();
        private static SetParentField _setParent = null;
        public static SetParentField SetParent
        {
            get
            {
                if (_setParent == null)
                {
                    lock (SetParentLock)
                    {
                        if (_setParent == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET private field "_parent".
                            FieldInfo setParentFieldInfo = TypeOfControl.GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance);
                            DynamicMethod dynamicMethod = new DynamicMethod("_parent", typeof(void), new Type[] { TypeOfControl, TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldarg_1);
                            ilGenerator.Emit(OpCodes.Stfld, setParentFieldInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _setParent = (SetParentField)dynamicMethod.CreateDelegate(typeof(SetParentField));
                        }
                    }
                }

                return _setParent;
            }
        }


        public delegate object GetEventKeyField(Control control);


        private static object EventInitLock = new object();
        private static GetEventKeyField _eventInit = null;
        public static GetEventKeyField EventInit
        {
            get
            {
                if (_eventInit == null)
                {
                    lock (EventInitLock)
                    {
                        if (_eventInit == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal static field "EventInit".
                            FieldInfo eventInitFieldInfo = TypeOfControl.GetField("EventInit", BindingFlags.NonPublic | BindingFlags.Static);
                            DynamicMethod dynamicMethod = new DynamicMethod("EventInit", typeof(object), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldfld, eventInitFieldInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _eventInit = (GetEventKeyField)dynamicMethod.CreateDelegate(typeof(GetEventKeyField));
                        }
                    }
                }

                return _eventInit;
            }
        }


        private static object EventLoadLock = new object();
        private static GetEventKeyField _eventLoad = null;
        public static GetEventKeyField EventLoad
        {
            get
            {
                if (_eventLoad == null)
                {
                    lock (EventLoadLock)
                    {
                        if (_eventLoad == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal static field "EventLoad".
                            FieldInfo eventLoadFieldInfo = TypeOfControl.GetField("EventLoad", BindingFlags.NonPublic | BindingFlags.Static);
                            DynamicMethod dynamicMethod = new DynamicMethod("EventLoad", typeof(object), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldfld, eventLoadFieldInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _eventLoad = (GetEventKeyField)dynamicMethod.CreateDelegate(typeof(GetEventKeyField));
                        }
                    }
                }

                return _eventLoad;
            }
        }


        private static object EventUnloadLock = new object();
        private static GetEventKeyField _eventUnload = null;
        public static GetEventKeyField EventUnload
        {
            get
            {
                if (_eventUnload == null)
                {
                    lock (EventUnloadLock)
                    {
                        if (_eventUnload == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal static field "EventUnload".
                            FieldInfo eventUnloadFieldInfo = TypeOfControl.GetField("EventUnload", BindingFlags.NonPublic | BindingFlags.Static);
                            DynamicMethod dynamicMethod = new DynamicMethod("EventUnload", typeof(object), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldfld, eventUnloadFieldInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _eventUnload = (GetEventKeyField)dynamicMethod.CreateDelegate(typeof(GetEventKeyField));
                        }
                    }
                }

                return _eventUnload;
            }
        }


        public virtual Delegate InitDelegate { get; set; }
        public virtual Delegate LoadDelegate { get; set; }
        public virtual Delegate UnloadDelegate { get; set; }


        private bool? _callbackMethodIsGlobal = null;
        public virtual bool CallbackMethodIsGlobal 
        {
            get
            {
                if (!_callbackMethodIsGlobal.HasValue)
                {
                    if (ParentPage == null) { return false; }

                    if (!ParentPage.IsCallback)
                    {
                        _callbackMethodIsGlobal = false;
                    }
                    else
                    {
                        if (ParentPage.Request.Form["__CALLBACKPARAM"] != null && ParentPage.Request.Form["__CALLBACKPARAM"].Contains("\"callbackMethod\":\"Global"))
                        {
                            _callbackMethodIsGlobal = true;
                        }
                        else
                        {
                            _callbackMethodIsGlobal = false;
                        }
                    }
                }

                return _callbackMethodIsGlobal.Value;
            }
        }


        private bool? _controlInvokedCallback = null;
        public virtual bool ControlInvokedCallback
        {
            get
            {
                if (!_controlInvokedCallback.HasValue)
                {
                    Control control = Control as Control;

                    if (control.UniqueID == null) { return false; }

                    if (control.UniqueID == ParentPage.Request.Form["__CALLBACKID"])
                    {
                        _controlInvokedCallback = true;
                    }
                    else
                    {
                        _controlInvokedCallback = false;
                    }
                }

                return _controlInvokedCallback.Value;
            }
        }


        public delegate void EnableEventValidationField(Page parentPage, bool enabled);


        private static object EnableEventValidationLock = new object();
        private static EnableEventValidationField _enableEventValidation = null;
        public static EnableEventValidationField EnableEventValidation
        {
            get
            {
                if (_enableEventValidation == null)
                {
                    lock (EnableEventValidationLock)
                    {
                        if (_enableEventValidation == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET private field "_enableEventValidation".
                            FieldInfo enableEventValidationFieldInfo = typeof(Page).GetField("_enableEventValidation", BindingFlags.NonPublic | BindingFlags.Instance);
                            DynamicMethod dynamicMethod = new DynamicMethod("_enableEventValidation", typeof(void), new Type[] { typeof(Page), typeof(bool) }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldarg_1);
                            ilGenerator.Emit(OpCodes.Stfld, enableEventValidationFieldInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _enableEventValidation = (EnableEventValidationField)dynamicMethod.CreateDelegate(typeof(EnableEventValidationField));
                        }
                    }
                }

                return _enableEventValidation;
            }
        }
        //------\\ Properties //--------------------------------------------//



        //------// Constructors \\------------------------------------------\\
		protected CallbackStrategy(Control control)
		{
			if (control == null) { throw new ArgumentNullException("control"); }
			if (!(control is IAjaxControl)) { throw new ArgumentException("A reference of type IAjaxControl was expected.", "control"); }

			Control = (IAjaxControl)control;
		}
		//------\\ Constructors //------------------------------------------//



		//------// Methods \\-----------------------------------------------\\
        // This method is here, rather than in a static method of AjaxControl along with 
        // AjaxControl.Initialize, because it become overrideable so implementors can 
        // have their way with it.
		public virtual void InitializeCallback()
		{
            Control control = Control as Control;

            if (!String.IsNullOrEmpty(HttpContext.Current.Request.Form["__CALLBACKID"]))
            {
                if (String.IsNullOrEmpty(HttpContext.Current.Request.Form["__EVENTVALIDATION"]))
                {
                    EnableEventValidation(ParentPage, false);
                }
            }


            if (CallbackMethodIsGlobal)
            {
                ParentPage.PreInit += new EventHandler(SetupControlEvents);
            }
            else
            {
                ParentPage.InitComplete += new EventHandler(LoadControlState); // Calling this here ensures that viewstate and control state are loaded after the Control's Init event, but before its Load event, right where it's supposed to be in the Page Lifecycle.
            }

            control.PreRender += new EventHandler(ControlPreRenderHandler);
		}


        protected virtual void SetupControlEvents(object sender, EventArgs args)
        {
            Control control = Control as Control;

            // Detach all InitEvent and LoadEvent handlers from the control. Hold on to these
            // handlers until the callback logic is invoked with a call to the "RaiseCallbackEvent" method.
            EventHandlerList handlerList = GetEvents(control);

            InitDelegate = handlerList[EventInit(control)];
            handlerList.RemoveHandler(EventInit(control), InitDelegate);

            LoadDelegate = handlerList[EventLoad(control)];
            handlerList.RemoveHandler(EventLoad(control), LoadDelegate);


            // *NOTE* Unfortunately, we can only detach Init and Load event handlers. Any OnInit or 
            // OnLoad overloads of the control will still be invoked. 
        }


        protected virtual void LoadControlState(object sender, EventArgs args)
        {
            Control control = Control as Control;

            if (Control.StateStrategy != null && control.EnableViewState && ParentPage.IsPostBack)
            {
                if (ParentPage.IsCallback)
                {
                    if (ControlInvokedCallback)
                    {
                        Control.StateStrategy.Load();
                    }
                }
                else
                {
                    Control.StateStrategy.Load();
                }
            }
        }


        protected virtual void ControlPreRenderHandler(object sender, EventArgs args)
        {
            if (!Control.IsUpdating && (Control as Page) == null) // Markers outside of the <html> tags (in the case of the Control in context being a Page) is invalid and causes rendering problems, so markers aren't rendered.
            {
                RenderingHelper.RenderControlWithMarkers(Control as Control, new RenderingHelper.BeforeRendering(delegate() 
                {
                    // Depending on what medium the control is configured to have its 
                    // state saved to, there may or may not be the need for the initial 
                    // state footprint to be persisted. In the case of the DiskStateStrategy, 
                    // if the state is not saved on initial load (!ParentPage.IsPostBack) then 
                    // no state will be retrieved upon any callbacks that follow since none 
                    // will exist.

                    // *Note* - We're doing this here so that the JsonStateStrategy and the 
                    // DiscStateStrategy classes can BOTH properly persist the initial control 
                    // state without having to write logic for each specific StateStrategy.
                    if (ParentPage != null && !ParentPage.IsCallback && Control.StateStrategy != null && (Control as Control).EnableViewState)
                    {
                        Control.StateStrategy.Save();
                    }


			        Scripter.RegisterFrameworkScript();
			        Scripter.RegisterControlScript();   
                }));
            }
        }


		/// <remarks>This is a default implementation and can be overriden.</remarks>
		public virtual void ProcessCallback<T>(string callbackArguments) where T : CallbackStrategyArguments
		{
            try
            {
                // 1.) Deserialize the callback arguments.
                T jsObject = Serializer.DeserializeCallbackArguments<T>(callbackArguments);


                // 2.) Process all of the callback arguments.
                if (jsObject.callbackMethod == null)
                {
                    // TODO: Throw a more descriptive exception stating that the "callbackMethod" argument is missing and is required.
                }
                else
                {
                    jsObject.callbackMethod = jsObject.callbackMethod.Trim();
                }


                if (jsObject.methodName != null)
                {
                    jsObject.methodName = jsObject.methodName.Trim();
                }


                if (jsObject.state == null)
                {
                    jsObject.state = new Dictionary<string, object>();
                }


                if (jsObject.parameters == null)
                {
                    jsObject.parameters = new object[0];
                }


                if (jsObject.controlType != null)
                {
                    jsObject.controlType = jsObject.controlType.Trim();
                }


                // 3.) Invoke the correct processing method based on the value of the "callbackMethod" argument.
                switch (jsObject.callbackMethod)
                {
                    case "Invoke":

                        if (jsObject.methodName == null)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument is missing and is required for the "Invoke" callback method.
                        }
                        else if (jsObject.methodName == String.Empty)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument may NOT be an empty string value.
                        }


                        CallbackResult = Invoke(jsObject);

                        break;

                    case "GlobalInvoke":

                        if (jsObject.controlType == null)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument is missing and is required for the "Invoke" callback method.
                        }
                        else if (jsObject.controlType == String.Empty)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument may NOT be an empty string value.
                        }

                        if (jsObject.methodName == null)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument is missing and is required for the "Invoke" callback method.
                        }
                        else if (jsObject.methodName == String.Empty)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument may NOT be an empty string value.
                        }


                        CallbackResult = GlobalInvoke(jsObject);

                        break;

                    case "Update":

                        CallbackResult = Update(jsObject);

                        break;

                    case "GlobalUpdate":

                        if (jsObject.controlType == null)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument is missing and is required for the "Invoke" callback method.
                        }
                        else if (jsObject.controlType == String.Empty)
                        {
                            // TODO: Throw a more descriptive exception stating that the "methodName" argument may NOT be an empty string value.
                        }


                        CallbackResult = GlobalUpdate(jsObject);

                        break;

                    default:

                        throw new System.Exception("There is no server method signature that matches that the client-side has invoked.");
                }
            }
            catch (System.ArgumentException argumentException)
            {
                if (argumentException.Message == new ResourceManager(typeof(System.Web.HttpApplication)).GetString("Redirect_not_allowed_in_callback"))
                {

                }
                else
                {
                    ExceptionOccurred = true;

                    CallbackResult = new Dictionary<string, object>();
                    CallbackResult["Exception"] = argumentException;
                }
            }
            catch (System.Exception exception)
            {
                ExceptionOccurred = true;

                CallbackResult = new Dictionary<string, object>();
                CallbackResult["Exception"] = exception;
            }
		}


		/// <remarks>This is a default implementation and can be overriden.</remarks>
		public virtual string GenerateCallbackResult()
		{
            if (ExceptionOccurred && CallbackResult["Exception"] != null && CallbackResult["Exception"] is System.Exception)
            {
                try
                {
                    HttpContext.Current.Response.Write("e" + Serializer.SerializeException((System.Exception)CallbackResult["Exception"]));
                    HttpContext.Current.Response.Flush();
                    HttpContext.Current.Response.End();
                }
                catch (System.Threading.ThreadAbortException)
                {
                    return String.Empty;
                }

                return String.Empty;
            }
            else
            {
                // Handle the RedirectOptions.
                if (ParentPage != null && !String.IsNullOrEmpty(ParentPage.Response.RedirectLocation))
                {
                    CallbackResult["RedirectOptions"] = new RedirectOptions(HttpContext.Current.Response.RedirectLocation);
                }
                else if (Control.AjaxContext.RedirectOptions != null && !String.IsNullOrEmpty(Control.AjaxContext.RedirectOptions.RedirectUrl))
                {
                    CallbackResult["RedirectOptions"] = Control.AjaxContext.RedirectOptions;
                }

                return Serializer.SerializeCallbackResult(CallbackResult);
            }
		}


        protected virtual Control CreateControl(string typeName)
        {
            return TypeCache.CreateObject(Type.GetType(typeName)) as Control;
        }


        protected virtual Control CreateControl(Type controlType)
        {
            return TypeCache.CreateObject(controlType) as Control;
        }
		//------\\ Methods //-----------------------------------------------//



		//------// Abstract Methods \\--------------------------------------\\
        protected abstract Control LoadGlobalControl(CallbackStrategyArguments args);
        protected abstract Control LoadControl(CallbackStrategyArguments args);

		protected abstract Dictionary<string, object> GlobalUpdate(CallbackStrategyArguments args);
        protected abstract Dictionary<string, object> Update(CallbackStrategyArguments args);

        protected abstract Dictionary<string, object> GlobalInvoke(CallbackStrategyArguments args);
        protected abstract Dictionary<string, object> Invoke(CallbackStrategyArguments args);
		//------\\ Abstract Methods //--------------------------------------//
	}
}
